package com.luo.d3s.anti.mvc.ec.service.impl;

import com.luo.d3s.anti.mvc.ec.dao.CategoryMapper;
import com.luo.d3s.anti.mvc.ec.dao.GoodsMapper;
import com.luo.d3s.anti.mvc.ec.model.dto.CategoryCreateDto;
import com.luo.d3s.anti.mvc.ec.model.dto.CategoryModifyDto;
import com.luo.d3s.anti.mvc.ec.model.dto.CategoryPageQueryDto;
import com.luo.d3s.anti.mvc.ec.entity.Category;
import com.luo.d3s.anti.mvc.ec.service.CategoryService;
import com.luo.d3s.anti.mvc.ec.model.vo.CategoryTreeVo;
import com.luo.d3s.anti.mvc.ec.model.vo.CategoryVo;
import com.luo.d3s.core.application.assmebler.BaseAssembler;
import com.luo.d3s.core.application.assmebler.PageResponseAssembler;
import com.luo.d3s.core.application.dto.MultiResponse;
import com.luo.d3s.core.application.dto.PageResponse;
import com.luo.d3s.core.application.dto.Response;
import com.luo.d3s.core.application.dto.SingleResponse;
import com.luo.d3s.core.util.validation.Validates;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 分类服务实现类
 *
 * @author luohq
 * @date 2023-01-04 17:06
 */
@Service
@CacheConfig(cacheNames = CategoryService.CATEGORY_TREE_CACHE_NAME)
@CacheEvict(allEntries = true)
public class CategoryServiceImpl implements CategoryService {

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private GoodsMapper goodsMapper;

    @Override
    public SingleResponse<CategoryVo> findCategory(Long categoryId) {
        Category category = this.categoryMapper.selectById(categoryId);
        return SingleResponse.of(BaseAssembler.convert(category, CategoryVo.class));
    }

    @Override
    public PageResponse<CategoryVo> findCategoryPage(CategoryPageQueryDto categoryPageQueryDto) {
        PageResponse<Category> pageResp = this.categoryMapper.findPage(categoryPageQueryDto);
        return PageResponseAssembler.toPageResp(pageResp, category -> BaseAssembler.convert(category, CategoryVo.class));
    }

    @Cacheable(key = "#p0?:'root'")
    @Override
    public MultiResponse<CategoryTreeVo> findCategoryTree(Long categoryId) {
        //查询分类ID对应的根分类TreeDto
        List<CategoryTreeVo> rootCategoryTreeVoList = this.categoryMapper.findRootCategoryTreeVoList(categoryId);
        //递归查询子分类列表
        this.extractCategoryTree(rootCategoryTreeVoList);
        //返回递归查询结果
        return MultiResponse.of(rootCategoryTreeVoList);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public SingleResponse<CategoryVo> createCategory(CategoryCreateDto categoryCreateDto) {
        //创建分类实体
        Category category = BaseAssembler.convert(categoryCreateDto, Category.class);
        //验证新增分类是否合法
        this.validateCategory(category);
        //保存分类
        this.categoryMapper.insert(category);
        //转换CategoryDto
        return SingleResponse.of(BaseAssembler.convert(category, CategoryVo.class));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response modifyCategory(CategoryModifyDto categoryModifyDto) {
        //验证分类ID是否存在
        Category existCategory = this.categoryMapper.selectById(categoryModifyDto.getId());
        Validates.notNull(existCategory, "category does not exist");
        //修改基础信息
        //修改基础信息
        Category category = BaseAssembler.convert(categoryModifyDto, Category.class);
        //验证待修改分类是否合法
        this.validateCategory(category);
        //修改分类
        this.categoryMapper.updateById(category);
        return Response.buildSuccess();
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response removeCategory(Long categoryId) {
        //调用领域服务查询当前分类的所有子分类ID
        List<Long> subCategoryIdsWithParentId = this.extractSubCategoryIds(categoryId, new ArrayList<>());

        //验证待删除的分类ID下不存在商品
        Boolean existGoodsInCategory = this.goodsMapper.existGoodsInCategories(subCategoryIdsWithParentId);
        Validates.isFalse(existGoodsInCategory, "exist goods in categories");

        //批量删除当前分类及其子分类
        this.categoryMapper.deleteBatchIds(subCategoryIdsWithParentId);
        return Response.buildSuccess();
    }

    /**
     * 递归查询子分类列表
     *
     * @param rootCategoryTreeVoList 上级分类列表
     */
    private void extractCategoryTree(List<CategoryTreeVo> rootCategoryTreeVoList) {
        //若上级分类列表为空，则直接返回
        if (CollectionUtils.isEmpty(rootCategoryTreeVoList)) {
            return;
        }
        //遍历上级分类列表，依次查询其子分类列表
        rootCategoryTreeVoList.stream()
                .forEach(curParentCategoryTreeDto -> {
                    //查询当前分类的子分类TreeDto列表
                    List<CategoryTreeVo> subCategoryTreeVoList = this.categoryMapper.findSubCategoryTreeVoList(curParentCategoryTreeDto.getId());
                    //设置当前分类的子分类TreeDto列表
                    curParentCategoryTreeDto.setSubCategories(subCategoryTreeVoList);
                    //递归设置子分类
                    extractCategoryTree(subCategoryTreeVoList);
                });
    }

    /**
     * 验证分类信息
     *
     * @param category 分类
     */
    private void validateCategory(Category category) {
        //验证分类名称唯一性
        //新增时（category.id为空）无需排除分类ID
        //修改时（category.id非空）需排除当前被修改的分类ID，即无需与自身分类名称进行比较
        Boolean existCategoryName = this.categoryMapper.existCategoryName(category.getCategoryName(), category.getId());
        Validates.isFalse(existCategoryName, "category name is repeated");

        //验证上级分类ID存在
        if(Objects.nonNull(category.getParentCategoryId())) {
            //验证分类ID是否存在
            Boolean existParentId = this.categoryMapper.existCategoryId(category.getParentCategoryId());
            Validates.isTrue(existParentId, "category id does not exist");
        }
    }

    /**
     * 内部处理 - 根据上级分类ID查询子分类ID列表（包括上级分类）
     *
     * @param parentCategoryId  上级分类ID
     * @param subCategoryIdList 所有及子分类ID列表
     * @return 子分类ID列表（包括上级分类）
     */
    private List<Long> extractSubCategoryIds(Long parentCategoryId, List<Long> subCategoryIdList) {
        //上级分类ID为空，则直接返回
        if (null == parentCategoryId) {
            return subCategoryIdList;
        }
        //记录当前非空分类ID
        subCategoryIdList.add(parentCategoryId);
        //查询上级分类对应的子分类列表
        List<Category> subCategoryList = this.categoryMapper.findByParentId(parentCategoryId);
        //递归处理子分类列表
        if (null != subCategoryList && !subCategoryList.isEmpty()) {
            subCategoryList.stream()
                    .map(Category::getId)
                    .forEach(categoryId -> this.extractSubCategoryIds(categoryId, subCategoryIdList));
        }
        //返回上级分类及所有子分类ID列表
        return subCategoryIdList;
    }
}
